#include "pch.h"
#include "Explosion.h"
#include "PlayerShip.h"
#include "Random.h"
#include "Ship.h"

#include <array>

namespace
{
   dxroids::Ship::Attributes get_basic_attrs()
   {
      dxroids::Ship::Attributes attrs;
      attrs.MaximumNumberOfShots = DXROIDS_PLAYER_SHIP_MAX_SHOTS;
      attrs.MaximumShotDistance = DXROIDS_PLAYER_SHIP_PHOTON_MAX_DISTANCE;
      attrs.MaximumVelocity = DXROIDS_PLAYER_SHIP_MAXVELOCITY;
      attrs.ThrustMagnitude = DXROIDS_PLAYER_SHIP_THRUST_MAGNITUDE;
      attrs.ShotVelocity = DXROIDS_PLAYER_SHIP_PHOTON_VELOCITY;
      return attrs;
   }

   dxroids::Ship::Attributes get_noahs_basic_attrs()
   {
      dxroids::Ship::Attributes attrs;
      attrs.MaximumNumberOfShots = DXROIDS_PLAYER_SHIP_MAX_SHOTS * 2;
      attrs.MaximumShotDistance = DXROIDS_PLAYER_SHIP_PHOTON_MAX_DISTANCE;
      attrs.MaximumVelocity = DXROIDS_PLAYER_SHIP_MAXVELOCITY;
      attrs.ThrustMagnitude = DXROIDS_PLAYER_SHIP_THRUST_MAGNITUDE;
      attrs.ShotVelocity = DXROIDS_PLAYER_SHIP_PHOTON_VELOCITY;
      return attrs;
   }
}

dxroids::PlayerShip::PlayerShip(Vector const & _pos, float _bounding_radius, float _scale)
   // : dxroids::PlayerShip(_pos, _bounding_radius, _scale, get_basic_attrs())
    : Ship(_pos, _scale, _bounding_radius, get_basic_attrs())
    , inHyperSpace_(false)
{
}

dxroids::PlayerShip::PlayerShip(Vector const & _pos, float _bounding_radius, float _scale, Ship::Attributes const & _attrs)
    : Ship(_pos, _scale, _bounding_radius, _attrs)
    , inHyperSpace_(false)
{
}


std::unique_ptr<dxroids::GameObject> dxroids::PlayerShip::Destroy(std::shared_ptr<SoundManager> const & _sound_manager)
{   
   SetIsActive(false);
   return Explosion::NewDefault(GetPosition());
}

DXROIDS_COM_PTR<ID2D1Geometry> dxroids::PlayerShip::CreateShipGeometry(
   DXROIDS_COM_PTR<ID2D1Factory> const & _factory, float _scale)
{   
   std::vector<D2D1_POINT_2F> points;
   points.push_back(D2D1::Point2F(0.0f, -20.0f));
   points.push_back(D2D1::Point2F(-15.0f, 20.0f));
   points.push_back(D2D1::Point2F(-5.0f, 10.0f));
   points.push_back(D2D1::Point2F(5.0f, 10.0f));
   points.push_back(D2D1::Point2F(15.0f, 20.0f));   
   return CreateScaledPathGeometry(_factory, points, _scale);
}

DXROIDS_COM_PTR<ID2D1Geometry> dxroids::PlayerShip::CreateThrustGeometry(
   DXROIDS_COM_PTR<ID2D1Factory> const & _factory, float _scale)
{
   std::vector<D2D1_POINT_2F> points;
   points.push_back(D2D1::Point2F(-5.0f, 15.0f));
   points.push_back(D2D1::Point2F(0.0f, 25.0f));
   points.push_back(D2D1::Point2F(5.0f, 15.0f));
   return CreateScaledPathGeometry(_factory, points, _scale);   
}

void dxroids::PlayerShip::EngageHyperspace()
{
   if (!IsActive())
   {
      return;
   }

   inHyperSpace_ = true;
   SetIsVisible(false);
   SetFireShotEnabled(false);

   RegisterTimedCallback(DXROIDS_PLAYER_SHIP_SECONDS_IN_HYPERSPACE, [&](GameTime const & _timer)
   {
      this->inHyperSpace_ = false;
      this->SetIsVisible(true);
      
      Random rand;

      float const offset_x = (((1.0f - DXROIDS_PLAYER_SHIP_HYPERSPACE_CONSTRAINT) * ScreenSize.x) / 2.0f);
      float const x = rand.NextFloat(offset_x, ScreenSize.x - offset_x);

      float const offset_y = (((1.0f - DXROIDS_PLAYER_SHIP_HYPERSPACE_CONSTRAINT) * ScreenSize.y) / 2.0f);
      float const y = rand.NextFloat(offset_y, ScreenSize.y - offset_y);

      Vector new_position(x, y);
      SetPosition(new_position);
      SetFireShotEnabled(true);

      // TODO: add chance of destruction on re-entry

      return false;
   });
}


dxroids::NoahsShip::NoahsShip(Vector const & _pos, float _scale)   
   : PlayerShip(_pos, 500.0f * _scale, _scale, get_noahs_basic_attrs())
   , shot_number_(0)
{

}


DXROIDS_COM_PTR<ID2D1Geometry> dxroids::NoahsShip::CreateShipGeometry( 
   DXROIDS_COM_PTR<ID2D1Factory> const & _factory, float _scale)
{    
   DXROIDS_COM_PTR<ID2D1PathGeometry> path_geometry;
   DXROIDS_COM_PTR<ID2D1GeometrySink> sink;

   ThrowIfFailed(_factory->CreatePathGeometry(&path_geometry));
   ThrowIfFailed(path_geometry->Open(&sink));

   // main body   
   sink->BeginFigure(make_point(-35, -50.0f, _scale), D2D1_FIGURE_BEGIN_FILLED);
   sink->AddLine(make_point(-15.0f, -70.0f, _scale));
   sink->AddLine(make_point(15.0f, -70.0f, _scale));
   sink->AddLine(make_point(35.0f, -50.0f, _scale));

   // right cannon
   sink->AddLine(make_point(35.0f, -20.0f, _scale));
   sink->AddLine(make_point(55.0f, -20.0f, _scale));
   sink->AddLine(make_point(65.0f, -40.0f, _scale)); 
   sink->AddLine(make_point(65.0f, -70.0f, _scale)); 
   sink->AddLine(make_point(75.0f, -90.0f, _scale));   
   sink->AddLine(make_point(85.0f, -90.0f, _scale));
   sink->AddLine(make_point(95.0f, -70.0f, _scale));   
   sink->AddLine(make_point(95.0f, -40.0f, _scale));
   sink->AddLine(make_point(65.0f, 0.0f, _scale));
   sink->AddLine(make_point(35.0f, 0.0f, _scale));   
   // end right cannon

   sink->AddLine(make_point(35.0f, 50.0f, _scale));
   sink->AddLine(make_point(-35.0f, 50.0f, _scale));

   // left cannon
   sink->AddLine(make_point(-35.0f, 0.0f, _scale));
   sink->AddLine(make_point(-65.0f, 0.0f, _scale));
   sink->AddLine(make_point(-95.0f, -40.0f, _scale));
   sink->AddLine(make_point(-95.0f, -70.0f, _scale));
   sink->AddLine(make_point(-85.0f, -90.0f, _scale));
   sink->AddLine(make_point(-75.0f, -90.0f, _scale));
   sink->AddLine(make_point(-65.0f, -70.0f, _scale)); 
   sink->AddLine(make_point(-65.0f, -40.0f, _scale));
   sink->AddLine(make_point(-55.0f, -20.0f, _scale));   
   sink->AddLine(make_point(-35.0f, -20.0f, _scale));
   // end lower left cannon

   sink->EndFigure(D2D1_FIGURE_END_CLOSED);

   // lower lower thrust nozzle
   sink->BeginFigure(make_point(-35.0f, 50.0f, _scale), D2D1_FIGURE_BEGIN_FILLED);
   sink->AddLine(make_point(-25.0f, 60.0f, _scale));
   sink->AddLine(make_point(-35.0f, 80.0f, _scale));
   sink->AddLine(make_point(35.0f, 80.0f, _scale));
   sink->AddLine(make_point(25.0f, 60.0f, _scale));
   sink->AddLine(make_point(35.0f, 50.0f, _scale));
   sink->EndFigure(D2D1_FIGURE_END_OPEN);   
   
   ThrowIfFailed(sink->Close());

   DXROIDS_COM_PTR<ID2D1EllipseGeometry> ellipse_geometry;   

   D2D1_ELLIPSE const ellipse = D2D1::Ellipse(D2D1::Point2F(0 * _scale, -20 * _scale), 15.0f * _scale, 25.0f * _scale);   
   ThrowIfFailed(_factory->CreateEllipseGeometry(&ellipse, &ellipse_geometry));

   // can't figure out how to use std::array<> in place of c-style
   ID2D1Geometry * geometries[2] =  { ellipse_geometry, path_geometry };

   DXROIDS_COM_PTR<ID2D1GeometryGroup> geometry_group;
   ThrowIfFailed(_factory->CreateGeometryGroup(D2D1_FILL_MODE_WINDING, geometries, 2, &geometry_group));

   return DXROIDS_COM_PTR<ID2D1Geometry>(geometry_group);
}


DXROIDS_COM_PTR<ID2D1Geometry> dxroids::NoahsShip::CreateThrustGeometry(
   DXROIDS_COM_PTR<ID2D1Factory> const & _factory, float _scale)
{      
   std::vector<D2D1_POINT_2F> points;
   points.push_back(D2D1::Point2F(-25.0f, 90.0f));
   points.push_back(D2D1::Point2F(-35.0f, 100.0f));
   points.push_back(D2D1::Point2F(0, 130.0f));
   points.push_back(D2D1::Point2F(35.0f, 100.0f));
   points.push_back(D2D1::Point2F(25.0f, 90.0f));   
   return CreateScaledPathGeometry(_factory, points, _scale); 
}

dxroids::Vector dxroids::NoahsShip::GetNextShotPosition() const
{
   Vector const & pos = GetPosition();
   float const scale = GetScale();
   float const y =  - (100.f * scale);
   float const x = ( shot_number_++ % 2 == 0 ) ? -(80.0f * scale) : (80.0f * scale);   
   return Vector(x, y).Rotate(DegreesToRadians(GetDirection())).Translate(pos.GetX(), pos.GetY());   
}
